package ft.ess.impl;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import ft.ess.util.FileUtil;
import ft.ess.util.SpeedUtil;
import ft.ess.util.U;

import javafx.application.Platform;

public class EchoServerSocketImpl implements Runnable
{

	private String _ip = null;
	private int _port = -1;
	private String _folder = null;
	private List<String> _base_path = null;
	private EchoSocketServerController _ctx = null;
	
	private volatile boolean _running = false;
	private ServerSocket server = null;
	
	/**
	 * 是否添加文件名前缀（主要用于避免文件名重复）
	 */
	public static boolean ESS_ISPREFIX_RENAME = false;
	
	/**
	 * 缓冲区大小
	 */
	public static final int ESS_MAX_SIZE = 1024 * 4;
	
	/**
	 * 缓冲区大小（long型，与 {@code ESS_MAX_SIZE } 一致）
	 */
	public static final long ESS_MAX_SIZE_L = 1024L * 4L;
	
	
	
	public EchoServerSocketImpl(EchoSocketServerController ctx, String ip, int port, String folder)
	{
		setCtx(ctx);
		setIP(ip);
		setPort(port);
		setFolder(folder);
	}
	
	/**
	 * 开始监听
	 */
	public void run()
	{
		Socket socket = null;
		long dt1 = 0l, dt2 = 0l;
		long tt0 = 0L, tt1 = 0L;
		
		while(_running)
		{
			try
			{
				Platform.runLater(new Runnable() {
					public void run() {
						_ctx.putMsg("准备接收新文件" );
					}
				});
				
//				List<Map<String,String>> al = null;
				server=new ServerSocket(_port);
				
				Platform.runLater(new Runnable() {
					public void run() {
						_ctx.setServer( server );
					}
				});
				
				
				socket=server.accept();
				
				tt0 = System.nanoTime();
				bytesTotal = 0L;
				
				InputStream is = socket.getInputStream();
				DataInputStream dis = new DataInputStream(is);
				
				// 文件总数
				int total_file_count = -1;
				total_file_count = dis.readInt();
				
				if(total_file_count>=0)
				{
					nReceiveFileCnt = total_file_count;
					final String send_msg = "预计接收文件总数：" +
							Long.toString(nReceiveFileCnt) ;
					Platform.runLater(new Runnable() {
						public void run() {
							_ctx.putMsg(send_msg);
						}
					});
				}
				
//				al = new ArrayList<Map<String,String>>();
				
				// 初始化已接收文件个数
				nFileCnt = 0L;
				
				for(int i=0; i<total_file_count; i++)
				{
					publishProgressEx2();
					
					String name = "";
					String rev_name = ""; // 将反斜杠 \ 转换为正斜杠 / 后的值
					
					long len = 0l;
					dt1 = System.nanoTime();
					name=dis.readUTF();
					len=dis.readLong();
					
					if(name!=null && name.length()>0 && len>0)
					{
						rev_name = FileUtil.convertBs2Slash(name);
						
						final String name_final = name;
						final String name_converted = rev_name;
						final long len_final = len;
						Platform.runLater(new Runnable() {
							public void run() {
								_ctx.putMsg("传入文件名: " + name_final +
										"\n斜杠文件名: " + name_converted +
										"\n传入字节数: "+Long.toString(len_final) );
							}
						});
						
						// 更新主界面，初始化
						initializeProgress(len);
					}
					
//					{
//						Map<String,String> m = new HashMap<String,String>();
//						m.put("name", name);
//						m.put("length", Long.toString(len));
//						
//						al.add(m);
//					}
					
					//保存用文件名
					String outFileName = "";
					
					// 如果重命名开关开启，则添加文件名前缀
					if(ESS_ISPREFIX_RENAME==true)
					{
						String randFileName = U.getCurrentDate()+"_"+
								U.getRandomNum(4);
						outFileName = outFileName + randFileName + "_";
					}
					
					//拼接用于保存的
					outFileName = outFileName + FileUtil.getFileNameInPath(rev_name, _base_path); // "/download"
					String outFilePath = FileUtil.pathAddFile(_folder,outFileName );
					
					if(outFilePath!=null && outFilePath.length()>0)
					{
						final String outFilePath_final = outFilePath;
						Platform.runLater(new Runnable() {
							public void run() {
								_ctx.putMsg("本地保存为: " + outFilePath_final );
							}
						});
					}
					
					FileUtil.ensureParentPath(outFilePath);
					File outFile = new File(outFilePath);
					
					//修改日期
					//outFile.setLastModified(0l);
					
					FileOutputStream fos = new FileOutputStream(outFile);
					
					byte[] buf = new byte[ESS_MAX_SIZE];
					
					long cnt = 0l;
					int ch = 0;
					
					// 文件还剩下多少字节
					long rm = len - (long)ch;
					
					// 本次可以读取多少字节
					int to_read_len = 0;
					
					if(rm>ESS_MAX_SIZE_L)
					{
						// 如果还剩下的字节数大于 ESS_MAX_SIZE
						// 则本次最多只读 ESS_MAX_SIZE 字节
						to_read_len = ESS_MAX_SIZE;
					}
					else
					{
						// 否则就可以把剩下的一次性读完
						to_read_len = (int)rm;
					}
					
					// 读取一次
					ch = dis.read(buf, 0, to_read_len);
					
					while(ch>0)
					{
						// 如果没有碰到结束，说明还要读一次
						// 那么先减少【文件剩下多少字节数】
						rm = rm - (int)ch;
						
						// 同上
						if(rm>ESS_MAX_SIZE_L)
						{
							to_read_len = ESS_MAX_SIZE;
						}
						else
						{
							to_read_len = (int)rm;
						}
						
//						// 将读出来的字节写入文件
						
//						System.out.println("ch="+ch);
//						System.out.println("to_read_len="+to_read_len);
						
						
						//fos.write(ch);
						fos.write(buf, 0, ch);
						
						cnt = cnt + (long)ch;
						
//						System.out.println("cnt="+cnt);
						
						// 试图更新主界面
						publishProgress( (long)ch );
						
						ch = dis.read(buf, 0, to_read_len);
					}
					
					dt2 = System.nanoTime();
					fos.close();
					
					{
						nFileCnt++;
						// 传输完毕
						finalizeProgress();
						
						final String msg_final = "文件共接收: "+Long.toString(cnt)+
								" 字节，耗时 " + formatDouble( ( (double)(dt2-dt1) ) / 1000000D ) +
								" 毫秒，速度 "+SpeedUtil.calcSpeed(cnt, dt1, dt2) +" 。";
						Platform.runLater(new Runnable() {
							public void run() {
								_ctx.putMsg( msg_final );
							}
						});
						
					}
					
				} // for total_file_count
				
				publishProgressEx2();
				
				dis.close();
				is.close();
				socket.close();
				server.close();
				
				tt1 = System.nanoTime();
				{
					final String msg_final = // "文件共接收: "+Long.toString(cnt)+
							"总字节：["+Long.toString(bytesTotal) + "]字节；" +
							"总耗时：["+ formatDouble( ( (double)(tt1-tt0) ) / 1000000000D ) + "]秒；" +
							"总速度：["+SpeedUtil.calcSpeed(bytesTotal, tt0, tt1) +"]";
					Platform.runLater(new Runnable() {
						public void run() {
							_ctx.putMsg( msg_final );
						}
					});
					
				}
			}
			catch(java.net.SocketException e)
			{
				// 如果正在 accept 但是被 close 就会触发该异常
				if(_running)
				{
					final Exception ex = e;
					Platform.runLater(new Runnable() {
						public void run() {
							_ctx.putMsg("有错误发生（java.net.SocketException）："+ex.getMessage());
						}
					});
					
					_running = false;
					
					/// DEBUG ///
					System.out.println("有错误发生（java.net.SocketException）");
					System.out.println(e.getMessage());
					e.printStackTrace();
					/// DEBUG ///
				}
				else
				{
					// 所以这里的报错是可以忽略的
					/// DEBUG ///
//					System.out.println("目测ServerSocket被关闭");
//					System.out.println(e.getMessage());
//					e.printStackTrace();
					/// DEBUG ///
				}
			}
			catch(Exception e)
			{
				if(_running)
				{
					final Exception ex = e;
					Platform.runLater(new Runnable() {
						public void run() {
							_ctx.putMsg("有错误发生："+ex.getMessage());
						}
					});
					
					_running = false;
				}
			}
		}
		
		{ /// DEBUG
			Platform.runLater(new Runnable() {
				public void run() {
					_ctx.putMsg("不再接收文件" );
				}
			});
		}
		
	}
	
	/**
	 * 试图更新主界面 progressBar : pg1
	 * @param sent 已传输字节数
	 */
	private void publishProgress(long sent)
	{
		if(_ctx!=null && sent>0L)
		{
			bytesTotal += sent;
			nSent += sent;
			nSum += sent;
			// 当前时间更新
			nTs2 = System.nanoTime();
			nCs2 = nTs2;
			
			// 判断，时间流逝超过了预定义的值，才真正去更新主进程界面
			if( (nTs2-nTs1) > _PUBLISH_INTERVAL)
			{
				publishProgressEx();
			}
			
			// 判断，时间流逝超过了预定义的值，才真正去计算下载速率
			if( (nCs2-nCs1) > _CALCULATE_SPEED)
			{
				calculateSpeedEx();
			}
		}
	}
	
	/**
	 * 开始接收循环时调用
	 * @param total
	 */
	private void initializeProgress(long total)
	{
		nSent = 0L;
		nSum = 0L;
		nTotal = total;
		nTs1 = System.nanoTime();
		nCs1 = nTs1;
		
		// 设置初始值
		if(_ctx!=null)
		{
			Platform.runLater(new Runnable()
			{
				@Override
				public void run()
				{
					_ctx.setPg1Val(0.0D);
					_ctx.setLblStatus("0.00 kb/s");
				}
			});
		}
	}
	
	/**
	 * 计算进度<br />
	 * 然后更新主线程的 pg1 控件状态
	 */
	private void publishProgressEx()
	{
		if(_ctx!=null && nTotal > 0)
		{
			// 已发送比例
			final double dt1 = ((double)nSent )/( (double)nTotal );
			
			if(nTs2>nTs1)
			{
				nTs1 = nTs2;
			}
			
			Platform.runLater(new Runnable()
			{
				@Override
				public void run()
				{
					_ctx.setPg1Val(dt1);
				}
			});
		}
	}
	
	/**
	 * 计算下载速率<br />
	 * 然后更新主线程的 lblStatus 控件状态
	 */
	private void calculateSpeedEx()
	{
		if(_ctx!=null)
		{
			// 计算速度 字节 / 时间流逝
			double dSp1 = 0D;
			
			// 判断 df1 必须为正数
			if(nCs2>nCs1)
			{
				// 根据 nTs2 - nTs1 计算时间流逝 单位秒
				double df1 = ( (double)( nCs2 - nCs1 ) ) / ( (double)_ONE_SECOND_NANOS );
				dSp1 = ( (double)nSum / 1024.0D ) / df1;
				// 取完即可更新时间起点
				nCs1 = nCs2;
			}
			
			// 取完速度即可重置
			nSum = 0L;
			
			// 速率
			final String sSp1 = formatDouble(dSp1) + " kb/s";
			
			Platform.runLater(new Runnable()
			{
				@Override
				public void run()
				{
					_ctx.setLblStatus(sSp1);
				}
			});
		}
	}
	
	// 总接收字节数
	private static long bytesTotal = 0L;
	
	private static long nSent = 0L;
	private static long nSum = 0L; // 实际接收字节总数
	private static long nFileCnt = 0L; // 实际接收文件个数
	
	/**
	 * 预计接收文件个数
	 */
	private static long nReceiveFileCnt = 0L;
	
	private static long nTotal = 0L;
	private static long nTs1 = 0L;
	private static long nTs2 = 0L;
	
	/**
	 * 时间间隔多少纳秒，才去实际更新主线程界面
	 */
	private static final long _PUBLISH_INTERVAL = 100000000L; // <-- 0.1 秒
	
	// 用于计算速率的时间戳
	private static long nCs1 = 0L;
	private static long nCs2 = 0L;
	
	/**
	 * 时间间隔多少纳秒，才去实际计算下载速率
	 */
	private static final long _CALCULATE_SPEED  = 2000000000L; // <-- 2 秒
	
	/**
	 * 一秒钟有多少纳秒
	 */
	private static final long _ONE_SECOND_NANOS = 1000000000L;
	
//	private void publishStartEx(String str)
//	{
//		if(str!=null && str.length()>0)
//		{
//			final String msg = str;
//			Platform.runLater(new Runnable()
//			{
//				@Override
//				public void run()
//				{
//					_ctx.setLblStatus(msg);
//				}
//			});
//		}
//	}
	
	/**
	 * 结束
	 */
	private void finalizeProgress()
	{
		if(_ctx!=null)
		{
//			final String rt_msg = "传输" + Long.toString(nFileCnt)+ " 个文件，共 " + Long.toString(nSum)+ " 字节。";
			final String rt_msg = "传输完成";
			Platform.runLater(new Runnable()
			{
				@Override
				public void run()
				{
					_ctx.setPg1Val(0.0D);
					_ctx.setLblStatus(rt_msg);
				}
			});
		}
	}
	
	/**
	 * 更新 已接收 文件个数与可接收文件总数
	 */
	private void publishProgressEx2()
	{
		if(_ctx!=null)
		{
			// 当前已接收文件个数 / 可接收文件总数
			final String lbl_msg2 = Long.toString(nFileCnt) + "/" + Long.toString(nReceiveFileCnt);
			// 计算百分比
			final double v2 = ( (double)nFileCnt )  / ( (double)nReceiveFileCnt );
			Platform.runLater(new Runnable()
			{
				@Override
				public void run()
				{
					_ctx.setPg2Val(v2);
					_ctx.setLblStatus2(lbl_msg2);
				}
			});
		}
		
	}
	
//	/**
//	 * 结束本次所有文件传输
//	 */
//	private void finalizeProgress2()
//	{
//		if(_ctx!=null)
//		{
////			final String rt_msg = "传输" + Long.toString(nFileCnt)+ " 个文件，共 " + Long.toString(nSum)+ " 字节。";
//			final String rt_msg = "传输完成";
//			Platform.runLater(new Runnable()
//			{
//				@Override
//				public void run()
//				{
//					_ctx.setPg1Val(0.0D);
//					_ctx.setLblStatus(rt_msg);
//				}
//			});
//		}
//	}
	
	public void setCtx(EchoSocketServerController ctx)
	{
		if(ctx!=null)
		{
			_ctx = ctx;
		}
	}
	
	public void setIP(String ip)
	{
		if(ip!=null && ip.length()>0)
		{
			_ip = ip;
		}
	}
	
	public void setPort(int port)
	{
		if(port >= 0 && port <= 65565)
		{
			_port = port;
		}
	}
	
	public void setFolder(String folder)
	{
		if(folder!=null && folder.length()>0)
		{
			_folder = folder;
		}
	}
	
	public List<String> getBasePath()
	{
		if(_base_path==null)
		{
			_base_path = new ArrayList<String>();
		}
		return _base_path;
	}
	
	public void addBasePath(String basePath)
	{
		if(basePath!=null && basePath.length()>0)
		{
			List<String> base = getBasePath();
			if(base.contains(basePath)==false)
			{
				base.add(basePath); // _base_path = basePath;
			}
		}
	}
	
	public void setFixPrefix(boolean fix)
	{
		ESS_ISPREFIX_RENAME = fix;
	}
	
	public void setRunning(boolean running)
	{
		_running = running;
	}
	
	/**
	 * 返回 ServerSocket 共 ctx 停止监听时使用
	 * @return
	 */
	public ServerSocket getServerSocketReference()
	{
		return server;
	}
	
	/**
	 * 格式化 double 小数，保留 2 位小数
	 * @param d
	 * @return
	 */
	private static String formatDouble(double d)
	{
		String ret = "";
		NumberFormat nf = NumberFormat.getNumberInstance();
		nf.setMaximumFractionDigits(2);
		ret = nf.format(d);
		return ret;
	}

}
